/*
    Copyright 2006 Andrew Wilkinson <andrew@indiegigs.co.uk>

    This file is part of libfixed.

    libfixed is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    libfixed is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with libfixed; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include <stdio.h>
#include <stdlib.h>

#include "fixed.h"

Fixed16 Fixed_fromFloat16(float f) {
    Fixed16 fix;
    float nf = (f < 0 ? -f : f);
    uint16_t mag = (int32_t)nf;
    uint16_t frac = (uint32_t)((nf-(float)((int32_t)nf)) * ((1<<16)-1));
    fix = (Fixed16) { mag << 16 | frac };
    if(f < 0) {
        fix = (Fixed16) { -fix.val };
    }
    return fix;
}

Fixed16 Fixed_fromDouble16(double f) {
    Fixed16 fix;
    float nf = (f < 0 ? -f : f);
    uint16_t mag = abs((int32_t)nf);
    uint16_t frac = (uint32_t)((nf-(double)((int32_t)nf)) * ((1<<16)-1));
    fix = (Fixed16) { mag << 16 | frac };
    if(f < 0) {
        fix = (Fixed16) { -fix.val };
    }
    return fix;
}

float Fixed_toFloat16(Fixed16 f) {
    float r = ((float)Fixed_frac16(f)) / (float)((1<<16)-1);
    r = r + Fixed_mag16(f);
    return r * Fixed_sign16(f);
}

double Fixed_toDouble16(Fixed16 f) {
    double r = ((double)Fixed_frac16(f)) / ((1<<16)-1);
    r = r + Fixed_mag16(f);
    return r * Fixed_sign16(f);
}

Fixed32 Fixed_fromFloat32(float f) {
    Fixed32 fix;
    float af = (f < 0 ? -f : f);
    int64_t mag = (int32_t)af;
    uint64_t frac = (uint64_t)((double)((double)af-(double)((uint32_t)af)) * (double)(((uint64_t)1<<32)-1));
    fix = (Fixed32) { mag << 32 | frac };
    if(f < 0) {
        fix = (Fixed32) { -fix.val };
    }
    return fix;
}

Fixed32 Fixed_fromDouble32(double f) {
    Fixed32 fix;
    int64_t mag = abs((int32_t)f);
    int32_t frac = (int32_t)((double)((double)f-(double)((int32_t)f)) * (double)(((int64_t)1<<32)-1));
    fix = (Fixed32) {mag << 32 | frac };
    if(f < 0) {
        fix = (Fixed32) { -fix.val };
    }
    return fix;
}

float Fixed_toFloat32(Fixed32 f) {
    float r = ((float)Fixed_frac32(f))/((float)((int64_t)1<<32)-1);
    r = r + Fixed_mag32(f);
    return r  * Fixed_sign32(f);
}

double Fixed_toDouble32(Fixed32 f) {
    double r = ((double)Fixed_frac32(f))/(((int64_t)1<<32)-1);
    r = r + Fixed_mag32(f);
    return r * Fixed_sign32(f);
}
